package com.getpebble.android.bluetooth.device;

import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import com.getpebble.android.PebbleApplication;
import com.getpebble.android.bluetooth.protocol.ProtocolMessage;
import com.getpebble.android.bluetooth.radio.PebbleBluetoothDevice;
import com.getpebble.android.bluetooth.radio.PebbleBluetoothDevice.BondInitFailedException;
import com.getpebble.android.bluetooth.radio.PebbleBluetoothSocket;
import com.getpebble.android.common.core.trace.Trace;
import com.getpebble.android.common.core.util.PblPreferences;
import com.getpebble.android.common.core.util.PblPreferences.PrefKey;
import com.getpebble.android.common.util.DiscoveryUtils;
import com.getpebble.android.framework.protocol.EndpointId;
import com.getpebble.android.framework.util.ByteUtils;
import com.sun.mail.imap.protocol.ListInfo;
import com.sun.mail.smtp.SMTPMessage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class RemoteDevice {
    static final int[] IN_STREAM_LOCK = new int[0];
    static final int[] OUT_STREAM_LOCK = new int[0];
    public static final String TAG = RemoteDevice.class.getSimpleName();
    PebbleBluetoothDevice mBluetoothDevice = null;
    PebbleBluetoothSocket mBluetoothSocket = null;
    InputStream mInputStream = null;
    boolean mInputTaskRunning = false;
    boolean mOneDodgyPairingExceptionCounted = false;
    private Handler mOutputHandler;
    private HandlerThread mOutputHandlerThread = null;
    OutputStream mOutputStream = null;
    boolean mOutputTaskRunning = false;
    private final PblPreferences mPrefs = new PblPreferences(PebbleApplication.getAppContext());
    private IRemoteDeviceCallback mRemoteDeviceCallback = null;
    private RemoteDeviceConnectTask mRemoteDeviceConnectTask = null;
    private RemoteDeviceInputTask mRemoteDeviceInputTask = null;
    protected DeviceStatus mStatus = DeviceStatus.DISCONNECTED;
    private boolean mWaitingForOutputToDisconnect = false;

    static /* synthetic */ class AnonymousClass4 {
        static final /* synthetic */ int[] $SwitchMap$com$getpebble$android$bluetooth$device$RemoteDevice$DeviceStatus = new int[DeviceStatus.values().length];

        static {
            try {
                $SwitchMap$com$getpebble$android$bluetooth$device$RemoteDevice$DeviceStatus[DeviceStatus.DISCONNECTED.ordinal()] = 1;
            } catch (NoSuchFieldError e) {
            }
            try {
                $SwitchMap$com$getpebble$android$bluetooth$device$RemoteDevice$DeviceStatus[DeviceStatus.CONNECTED.ordinal()] = 2;
            } catch (NoSuchFieldError e2) {
            }
            try {
                $SwitchMap$com$getpebble$android$bluetooth$device$RemoteDevice$DeviceStatus[DeviceStatus.CONNECTING.ordinal()] = 3;
            } catch (NoSuchFieldError e3) {
            }
        }
    }

    enum DeviceStatus {
        DISCONNECTED,
        CONNECTING,
        CONNECTED
    }

    private static class MessageTooLargeException extends Exception {
        MessageTooLargeException(String msg) {
            super(msg);
        }
    }

    protected synchronized void setStatus(DeviceStatus status, RemoteDeviceEvent callbackEvent, ConnectionResult connectionResult) {
        Trace.debug(TAG, "setStatus: " + status);
        if (!this.mStatus.equals(status)) {
            if (DeviceStatus.DISCONNECTED.equals(status) && (this.mInputTaskRunning || this.mOutputTaskRunning)) {
                Trace.debug(TAG, "Not marking disconnected: mInputTaskRunning = " + this.mInputTaskRunning + " mOutputTaskRunning = " + this.mOutputTaskRunning);
                if (this.mOutputTaskRunning) {
                    this.mWaitingForOutputToDisconnect = true;
                }
            } else {
                this.mStatus = status;
                this.mWaitingForOutputToDisconnect = false;
                if (callbackEvent != null) {
                    fireCallback(callbackEvent, connectionResult);
                }
            }
        }
    }

    protected RemoteDevice(String address, IRemoteDeviceCallback callback) throws IllegalArgumentException, IllegalStateException {
        if (TextUtils.isEmpty(address)) {
            throw new IllegalArgumentException("'address' cannot be null!");
        }
        this.mBluetoothDevice = getDevice(address);
        if (this.mBluetoothDevice == null) {
            throw new IllegalStateException("Cannot locate device with address: " + address);
        }
        this.mRemoteDeviceCallback = callback;
    }

    protected PebbleBluetoothDevice getDevice(String address) {
        return AbsDeviceService.getPebbleBluetoothAdapter().getPebbleBluetoothDevice(address);
    }

    public String getAddress() {
        return this.mBluetoothDevice.getAddress();
    }

    public String getName() {
        return this.mBluetoothDevice.getName();
    }

    public String getName(Context context) {
        String btName = this.mBluetoothDevice.getName();
        return btName != null ? btName : DiscoveryUtils.getPebbleDisplayName(null, getAddress(), context);
    }

    boolean createBond() throws BondInitFailedException {
        return this.mBluetoothDevice.createBond();
    }

    void createSocket() throws IOException {
        Trace.debug(TAG, "createSocket() for " + this);
        if (this.mBluetoothSocket != null) {
            Trace.error(TAG, "Existing socket reference was non-null");
            this.mBluetoothSocket.close();
        }
        this.mBluetoothSocket = this.mBluetoothDevice.createSocket();
        if (this.mBluetoothSocket == null) {
            Trace.error(TAG, "Created null socket (contract violation)");
            throw new IOException("Failed to create IO socket connection!");
        }
    }

    boolean connect(Context context) {
        Trace.debug(TAG, "connect()");
        if (this.mStatus.equals(DeviceStatus.DISCONNECTED)) {
            this.mRemoteDeviceConnectTask = new RemoteDeviceConnectTask(context, this) {
                void onConnectTaskSuccess() {
                    RemoteDevice.this.setStatus(DeviceStatus.CONNECTED, RemoteDeviceEvent.CONNECT_RESULT, ConnectionResult.SUCCESS);
                    RemoteDevice.this.mInputTaskRunning = true;
                    RemoteDevice.this.mOutputTaskRunning = false;
                    RemoteDevice.this.mWaitingForOutputToDisconnect = false;
                }

                void onConnectTaskFailure(ConnectionResult failureReason) {
                    try {
                        RemoteDevice.this.closeConnectionResources();
                    } catch (IOException e) {
                        Trace.debug(TAG, "Exception closing resources after failied connection");
                    }
                    RemoteDevice.this.setStatus(DeviceStatus.DISCONNECTED, RemoteDeviceEvent.CONNECT_RESULT, failureReason);
                }
            };
            setStatus(DeviceStatus.CONNECTING, null, null);
            this.mRemoteDeviceConnectTask.submit();
            return true;
        }
        Trace.warning(TAG, "... is " + this.mStatus + "; not connecting");
        return false;
    }

    void connectToSocket() throws IOException, NullPointerException {
        Trace.debug(TAG, "connectToSocket()");
        if (this.mBluetoothSocket != null) {
            this.mBluetoothSocket.connect();
            this.mInputStream = this.mBluetoothSocket.getInputStream();
            this.mOutputStream = this.mBluetoothSocket.getOutputStream();
            this.mRemoteDeviceInputTask = createInputTask();
            this.mRemoteDeviceInputTask.submit();
            this.mOutputHandlerThread = new HandlerThread("output_" + getAddress());
            this.mOutputHandlerThread.start();
            this.mOutputHandler = new Handler(this.mOutputHandlerThread.getLooper());
            return;
        }
        Trace.error(TAG, "Failed to create a socket; bluetooth socket is null!");
        throw new IOException("Failed to create a socket; bluetooth socket is null!");
    }

    protected RemoteDeviceInputTask createInputTask() {
        return new RemoteDeviceInputTask(this) {
            public void onDisconnect() {
                Trace.info(RemoteDevice.TAG, "Input task disconnected");
                synchronized (RemoteDevice.this) {
                    try {
                        RemoteDevice.this.closeConnectionResources();
                    } catch (IOException e) {
                        Trace.debug(RemoteDevice.TAG, "Exception thrown when disconnecting device", e);
                    }
                    RemoteDevice.this.mInputTaskRunning = false;
                    RemoteDevice.this.setStatus(DeviceStatus.DISCONNECTED, RemoteDeviceEvent.DISCONNECTED, null);
                }
            }

            void onMessageReceived(ProtocolMessage message) {
                long start = System.currentTimeMillis();
                RemoteDevice.this.fireCallback(RemoteDeviceEvent.DATA_RECEIVED, message, null);
                long cbTime = System.currentTimeMillis() - start;
                if (cbTime > 100) {
                    Trace.verbose(RemoteDevice.TAG, "bt callbacks for " + EndpointId.fromCode(message.getEndpointId()) + " took " + cbTime + "ms");
                }
            }
        };
    }

    void killSocket() {
        Trace.verbose(TAG, "killSocket()");
        try {
            if (this.mInputStream != null) {
                this.mInputStream.close();
            }
        } catch (IOException e) {
            Trace.debug(TAG, "Error closing input stream", e);
        }
        try {
            if (this.mOutputStream != null) {
                this.mOutputStream.close();
            }
        } catch (IOException e2) {
            Trace.debug(TAG, "Error closing output stream", e2);
        }
        try {
            if (this.mBluetoothSocket != null) {
                this.mBluetoothSocket.close();
            }
        } catch (IOException e22) {
            Trace.debug(TAG, "Error closing output stream", e22);
        }
    }

    void closeConnectionResources() throws IOException {
        Trace.debug(TAG, "closeConnectionResources()");
        if (this.mRemoteDeviceInputTask != null) {
            this.mRemoteDeviceInputTask.stopInput();
            this.mRemoteDeviceInputTask = null;
        }
        IOException ioe = null;
        try {
            if (this.mInputStream != null) {
                this.mInputStream.close();
                this.mInputStream = null;
            }
        } catch (IOException e) {
            ioe = e;
            Trace.debug(TAG, "Error closing input stream", e);
        }
        try {
            if (this.mOutputStream != null) {
                this.mOutputStream.close();
                this.mOutputStream = null;
            }
        } catch (IOException e2) {
            ioe = e2;
            Trace.debug(TAG, "Error closing output stream", e2);
        }
        try {
            if (this.mBluetoothSocket != null) {
                this.mBluetoothSocket.close();
                this.mBluetoothSocket = null;
            }
        } catch (IOException e22) {
            ioe = e22;
            Trace.debug(TAG, "Error closing output stream", e22);
        }
        if (this.mOutputHandler != null) {
            this.mOutputHandler.removeCallbacksAndMessages(null);
            this.mOutputHandler = null;
        }
        if (this.mOutputHandlerThread != null) {
            this.mOutputHandlerThread.quit();
            this.mOutputHandlerThread = null;
        }
        if (ioe != null) {
            throw ioe;
        }
    }

    boolean disconnect() {
        Trace.debug(TAG, "disconnect()");
        switch (AnonymousClass4.$SwitchMap$com$getpebble$android$bluetooth$device$RemoteDevice$DeviceStatus[this.mStatus.ordinal()]) {
            case SMTPMessage.RETURN_FULL /*1*/:
                Trace.debug(TAG, "Already disconnected...");
                return false;
            case SMTPMessage.RETURN_HDRS /*2*/:
                killSocket();
                return true;
            case ListInfo.INDETERMINATE /*3*/:
                this.mRemoteDeviceConnectTask.cancel();
                killSocket();
                return true;
            default:
                return false;
        }
    }

    void sendMessage(final ProtocolMessage protocolMessage) {
        if (this.mOutputHandler == null) {
            Trace.warning(TAG, "Output handler is null");
        } else {
            this.mOutputHandler.post(new Runnable() {
                public void run() {
                    boolean disconnected = false;
                    boolean success = false;
                    synchronized (RemoteDevice.this) {
                        RemoteDevice.this.mOutputTaskRunning = true;
                        RemoteDevice.this.mWaitingForOutputToDisconnect = false;
                    }
                    try {
                        Trace.debug(RemoteDevice.TAG, "size: " + protocolMessage.getDataSize());
                        ByteBuffer message = ByteBuffer.allocate(protocolMessage.getDataSize() + 4);
                        message.putShort((short) (protocolMessage.getDataSize() & 65535));
                        message.putShort((short) (protocolMessage.getEndpointId() & 65535));
                        message.put(protocolMessage.getDataBuffer());
                        if (RemoteDevice.this.mPrefs.getBooleanData(PrefKey.HEX_DUMP, false)) {
                            Trace.verbose(RemoteDevice.TAG, "Sending " + ByteUtils.hexDump(message.array()));
                        }
                        RemoteDevice.this.sendOutputMessage(message.array());
                        success = true;
                    } catch (IOException e) {
                        Trace.error(RemoteDevice.TAG, "Error sending data", e);
                        disconnected = true;
                    } catch (NullPointerException npe) {
                        Trace.error(RemoteDevice.TAG, "Error sending data", npe);
                    } catch (MessageTooLargeException e2) {
                        Trace.error(RemoteDevice.TAG, "Error sending data", e2);
                    }
                    if (success) {
                        RemoteDevice.this.fireCallback(RemoteDeviceEvent.DATA_SEND_SUCCESS);
                    } else {
                        if (disconnected) {
                            try {
                                RemoteDevice.this.closeConnectionResources();
                            } catch (IOException e3) {
                                Trace.debug(RemoteDevice.TAG, "Error closing connection resources (on output failure)");
                            }
                            synchronized (RemoteDevice.this) {
                                RemoteDevice.this.mOutputTaskRunning = false;
                                RemoteDevice.this.setStatus(DeviceStatus.DISCONNECTED, RemoteDeviceEvent.DISCONNECTED, null);
                            }
                        }
                        RemoteDevice.this.fireCallback(RemoteDeviceEvent.DATA_SEND_FAILED);
                    }
                    synchronized (RemoteDevice.this) {
                        RemoteDevice.this.mOutputTaskRunning = false;
                        if (RemoteDevice.this.mWaitingForOutputToDisconnect) {
                            RemoteDevice.this.setStatus(DeviceStatus.DISCONNECTED, RemoteDeviceEvent.DISCONNECTED, null);
                        }
                    }
                }
            });
        }
    }

    void fireCallback(RemoteDeviceEvent event) {
        fireCallback(event, null);
    }

    void fireCallback(RemoteDeviceEvent event, ConnectionResult result) {
        fireCallback(event, null, result);
    }

    void fireCallback(RemoteDeviceEvent event, ProtocolMessage message, ConnectionResult result) {
        if (this.mRemoteDeviceCallback != null) {
            this.mRemoteDeviceCallback.onEvent(this, event, message, result);
        }
    }

    int readInputMessage(byte[] data, int byteOffset, int byteCount) throws IOException {
        int read;
        synchronized (IN_STREAM_LOCK) {
            read = this.mInputStream.read(data, byteOffset, byteCount);
        }
        return read;
    }

    void sendOutputMessage(byte[] message) throws IOException, MessageTooLargeException {
        if (message.length > 2048) {
            throw new MessageTooLargeException("Message is to large (" + message.length + " bytes)");
        }
        synchronized (OUT_STREAM_LOCK) {
            this.mOutputStream.write(message);
        }
    }

    public DeviceStatus getStatus() {
        return this.mStatus;
    }

    public RemoteDeviceMetaData getMetaData(Context context) {
        RemoteDeviceMetaData remoteDeviceMetaData = new RemoteDeviceMetaData();
        remoteDeviceMetaData.setName(getName(context));
        remoteDeviceMetaData.setMacAddress(getAddress());
        remoteDeviceMetaData.setConnected(this.mStatus.equals(DeviceStatus.CONNECTED));
        return remoteDeviceMetaData;
    }

    boolean isBonded() {
        return this.mBluetoothDevice.isBonded();
    }

    void acceptPairingRequest() {
        this.mBluetoothDevice.acceptPairingRequest();
    }

    boolean removeBond() {
        return this.mBluetoothDevice.removeBond();
    }
}
